package com.example.java_gobang.api;

import com.example.java_gobang.common.AppVariable;
import com.example.java_gobang.game.*;
import com.example.java_gobang.model.User;
import com.example.java_gobang.model.UserMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;

@Component
public class GameAPI extends TextWebSocketHandler {
    private ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private RoomManager roomManager;

    @Autowired
    private OnlineUserManger onlineUserManger;

    @Resource
    private UserMapper userMapper;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse resp = new GameReadyResponse();

        // 1. 先获取到用户的身份信息
        User user = (User) session.getAttributes().get(AppVariable.USER_SESSION_KEY);
        if(user == null){
            resp.setOk(false);
            resp.setReason("用户尚未登录");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }

        // 2. 判定当前用户是否已经进入房间(使用房间管理器进行查询)
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if (room == null){
            // 如果为 null, 当前没有找到对应的房间，该玩家还没有匹配到
            resp.setOk(false);
            resp.setReason("用户尚未匹配到！");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }

        // 3. 判定是否为多开
        if(onlineUserManger.getFromGameHall(user.getUserId()) != null
            || onlineUserManger.getFromGameRoom(user.getUserId()) != null){
            // 当相同的账号一个在游戏大厅，一个在游戏房间也要视为多开
            resp.setOk(true);
            resp.setReason("检测到该账号已经在线了");
            resp.setMessage("repeatConnection");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }

        // 4. 设置当前玩家上线
        onlineUserManger.enterGameRoom(user.getUserId(), session);

        // 5. 把玩家加入到游戏房间中
        //    当前逻辑是在 game_room.html 页面加载时进行的
        //    前面创建房间/匹配过程，是在 game_hall.html 页面中完成的
        //    执行到当前逻辑说明玩家已经页面跳转成功了
        synchronized (room){
            if(room.getUser1() == null){
                room.setUser1(user);
                // 设置此玩家为先手方
                room.setWhiteUser(user.getUserId());
                System.out.println("玩家1: " + user.getUsername() + "准备就绪");
                return;
            }
            if(room.getUser2() == null){
                room.setUser2(user);
                System.out.println("玩家2: " + user.getUsername() + "准备就绪");

                // 当两个玩家都加入成功后，服务器给这两名玩家返回 websocket 的信息
                // 告知玩家双方都准备就绪
                // 告知玩家1
                noticeGameReady(room, room.getUser1(), room.getUser2());
                // 告知玩家2
                noticeGameReady(room, room.getUser2(), room.getUser1());
                return;
            }
        }


        //

    }

    private void noticeGameReady(Room room, User thisuser, User thatuser) throws IOException {
        GameReadyResponse resp = new GameReadyResponse();
        resp.setMessage("gameReady");
        resp.setOk(true);
        resp.setReason("");
        resp.setRoomId(room.getRoomId());
        resp.setThisUserId(thisuser.getUserId());
        resp.setThatUserId(thatuser.getUserId());
        resp.setWhiteUser(room.getWhiteUser());

        WebSocketSession webSocketSession = onlineUserManger.getFromGameRoom(thisuser.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 得到用户信息
        User user = (User) session.getAttributes().get(AppVariable.USER_SESSION_KEY);
        if(user == null){
            System.out.println("[handleTextMessage]未登录！");
            return;
        }

        // 根据玩家 id 获取到房间对象
        Room room = roomManager.getRoomByUserId(user.getUserId());

        // 通过 room 对象来处理这次具体的请求
        room.putChess(message.getPayload());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        User user = (User) session.getAttributes().get(AppVariable.USER_SESSION_KEY);
        if(user == null){
            return;
        }
        WebSocketSession exitSession = onlineUserManger.getFromGameRoom(user.getUserId());
        if(session == exitSession) {
            // 目的是避免在多开的情况下，第二个用户退出连接导致第一个用户也退出
            onlineUserManger.exitGameRoom(user.getUserId());
        }
        System.out.println("当前用户: "+ user.getUsername() + "游戏房间连接异常");
        //通知对手获胜了
        noticeThatUserWin(user);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User) session.getAttributes().get(AppVariable.USER_SESSION_KEY);
        if(user == null){
            return;
        }
        WebSocketSession exitSession = onlineUserManger.getFromGameRoom(user.getUserId());
        if(session == exitSession) {
            // 目的是避免在多开的情况下，第二个用户退出连接导致第一个用户也退出
            onlineUserManger.exitGameRoom(user.getUserId());
        }
        System.out.println("当前用户: "+ user.getUsername() + "离开游戏房间");
        //通知对手获胜了
        noticeThatUserWin(user);
    }

    private void noticeThatUserWin(User user) throws IOException {
        // 1. 根据当前玩家，找到玩家所在的房间
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if(room == null){
            System.out.println("当前房间已解散，无需通知对手！");
            return;
        }

        // 2. 根据房间找到对手
        User thatUser = (user == room.getUser1()) ? room.getUser2() : room.getUser1();

        // 3. 判断对手在线状态
        WebSocketSession webSocketSession = onlineUserManger.getFromGameRoom(thatUser.getUserId());
        if(webSocketSession == null){
            System.out.println("对手也掉线了，无需通知！");
            return;
        }

        // 4. 构造响应通知对手为获胜方
        GameResponse resp = new GameResponse();
        resp.setMessage("putChess");
        resp.setUserId(thatUser.getUserId());
        resp.setWinner(thatUser.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));

        // 5. 分数结算
        int winUserId = thatUser.getUserId();
        int loseUserId = user.getUserId();
        userMapper.userWin(winUserId);
        userMapper.userLose(loseUserId);

        // 6. 释放房间
        roomManager.remove(room.getRoomId(), room.getUser1().getUserId(), room.getUser2().getUserId());
    }
}
